package com.lining.orangeservice.entity.abstractentity;

import com.lining.orangeservice.common.enums.OrderStatus;
import com.lining.orangeservice.common.enums.PaymentMethod;
import com.lining.orangeservice.entity.po.OrderItem;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * 订单抽象基类，定义了订单的基本属性与行为规范。
 * <p>
 * 该类是所有订单类型的基类，定义了订单的通用属性和方法，同时声明了需要子类实现的抽象方法。
 * 通过继承该类，可以实现不同类型订单的特殊业务逻辑，如价格计算、取消规则等。
 * </p>
 */
public abstract class Order {
    /** 订单唯一标识 */
    protected Long orderId;
    /** 订单编号 */
    protected String orderNo;
    /** 用户ID */
    protected Long userId;
    /** 订单总金额 */
    protected BigDecimal totalAmount;
    /** 折扣金额 */
    protected BigDecimal discountAmount;
    /** 实际应付金额 */
    protected BigDecimal actualAmount;
    /** 订单状态 */
    protected OrderStatus status;
    /** 支付方式 */
    protected PaymentMethod paymentMethod;
    /** 订单创建时间 */
    protected LocalDateTime createTime;
    /** 订单更新时间 */
    protected LocalDateTime updateTime;
    /** 订单项列表 */
    protected List<OrderItem> items;
    
    /**
     * 抽象方法 - 计算订单金额
     * <p>
     * 不同类型的订单有不同的计算逻辑，需要子类根据具体业务规则实现该方法。
     * 例如普通订单可能有满减优惠，团购订单可能有固定折扣等。
     * </p>
     */
    public abstract void calculateAmount();
    
    /**
     * 抽象方法 - 判断订单是否可以取消
     * <p>
     * 不同类型的订单有不同的取消条件，需要子类根据具体业务规则实现该方法。
     * 例如秒杀订单可能不允许取消，团购订单可能在成团前可以取消等。
     * </p>
     * 
     * @return true-可以取消，false-不可以取消
     */
    public abstract boolean canCancel();
    
    /**
     * 抽象方法 - 判断订单是否可以退款
     * <p>
     * 不同类型的订单有不同的退款条件，需要子类根据具体业务规则实现该方法。
     * 例如已发货的订单可能不能退款，秒杀订单可能不允许退款等。
     * </p>
     * 
     * @return true-可以退款，false-不可以退款
     */
    public abstract boolean canRefund();
    
    /**
     * 通用方法 - 添加订单项
     * <p>
     * 向订单中添加一个新的订单项，并重新计算订单金额。
     * 如果订单项列表为空，则先初始化列表。
     * </p>
     * 
     * @param item 订单项对象
     */
    public void addItem(OrderItem item) {
        if (items == null) {
            items = new ArrayList<>();
        }
        items.add(item);
        calculateAmount(); // 添加商品后重新计算金额
    }
    
    /**
     * 通用方法 - 移除订单项
     * <p>
     * 根据订单项ID从订单中移除指定的订单项，并重新计算订单金额。
     * 如果订单项列表为空，则不执行任何操作。
     * </p>
     * 
     * @param itemId 订单项ID
     */
    public void removeItem(Long itemId) {
        if (items != null) {
            items.removeIf(item -> item.getItemId().equals(itemId));
            calculateAmount();
        }
    }
    
    /**
     * 通用方法 - 更新订单状态
     * <p>
     * 更新订单状态并设置更新时间。
     * 该方法不包含业务逻辑校验，仅负责状态变更和时间更新。
     * </p>
     * 
     * @param newStatus 新的订单状态
     */
    public void updateStatus(OrderStatus newStatus) {
        this.status = newStatus;
        this.updateTime = LocalDateTime.now();
    }
    
    /**
     * 通用方法 - 订单支付
     * <p>
     * 处理订单支付逻辑，只能对"待支付"状态的订单进行支付操作。
     * 支付成功后更新订单支付方式和状态为"已支付"。
     * </p>
     * 
     * @param method 支付方式
     * @return true-支付成功
     * @throws IllegalStateException 当订单状态不是"待支付"时抛出异常
     */
    public boolean pay(PaymentMethod method) {
        if (status != OrderStatus.PENDING) {
            throw new IllegalStateException("只有待支付订单才能支付");
        }
        this.paymentMethod = method;
        updateStatus(OrderStatus.PAID);
        return true;
    }
    
    // getters and setters
    public Long getOrderId() {
        return orderId;
    }

    public void setOrderId(Long orderId) {
        this.orderId = orderId;
    }

    public String getOrderNo() {
        return orderNo;
    }

    public void setOrderNo(String orderNo) {
        this.orderNo = orderNo;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public BigDecimal getTotalAmount() {
        return totalAmount;
    }

    public void setTotalAmount(BigDecimal totalAmount) {
        this.totalAmount = totalAmount;
    }

    public BigDecimal getDiscountAmount() {
        return discountAmount;
    }

    public void setDiscountAmount(BigDecimal discountAmount) {
        this.discountAmount = discountAmount;
    }

    public BigDecimal getActualAmount() {
        return actualAmount;
    }

    public void setActualAmount(BigDecimal actualAmount) {
        this.actualAmount = actualAmount;
    }

    public OrderStatus getStatus() {
        return status;
    }

    public void setStatus(OrderStatus status) {
        this.status = status;
    }

    public PaymentMethod getPaymentMethod() {
        return paymentMethod;
    }

    public void setPaymentMethod(PaymentMethod paymentMethod) {
        this.paymentMethod = paymentMethod;
    }

    public LocalDateTime getCreateTime() {
        return createTime;
    }

    public void setCreateTime(LocalDateTime createTime) {
        this.createTime = createTime;
    }

    public LocalDateTime getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(LocalDateTime updateTime) {
        this.updateTime = updateTime;
    }

    public List<OrderItem> getItems() {
        return items;
    }

    public void setItems(List<OrderItem> items) {
        this.items = items;
    }
}